//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  Low level interfaces that allow separate components to use objects that are defined
//               in completely independant libraries so long as they satisfy the basic interface requirements.
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either express or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is MapWindow.Interfaces
//
// The Initial Developer of this Original Code is Ted Dunsford. Created in August, 2007.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************
using System;
using System.Collections.Generic;
using System.Text;
using MapWindow.Data;
using System.Drawing;
using MapWindow.Main.EventArguments;
using MapWindow.Geometries;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;

namespace MapWindow.Drawing
{
    /// <summary>
    /// This represents a point feature that also has drawing characteristics
    /// </summary>
    public class MapPoint: MapFeature, IMapPoint
    {
        #region Variables

        /// <summary>
        /// Drawing properties for this point
        /// </summary>
        protected IPointSymbolizer _PointSymbolizer;
        
        /// <summary>
        /// The Parent Layer for this point
        /// </summary>
        protected IMapPointLayer _Parent;
        
        /// <summary>
        /// The Position for this point
        /// </summary>
        protected System.Drawing.PointF _Position;
        
        /// <summary>
        /// The Basic Point that lies at the heart of this mapPoint
        /// </summary>
        protected IBasicPoint _BasicPoint;
        
        
         /// <summary>
         /// Creates a completely new Point from this MapPoint
         /// </summary>
         /// <returns></returns>
        public virtual IPoint Copy()
        {
            return new MapWindow.Geometries.Point(X, Y, Z);
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Constructor for MapPoint
        /// </summary>
        public MapPoint()
        {
            _Position = System.Drawing.PointF.Empty;
            _PointSymbolizer = new PointSymbolizer();
            FeatureSymbolizer = _PointSymbolizer;
            _IsEmpty = true;
        }

        /// <summary>
        /// Beware that setting this position 
        /// </summary>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        public MapPoint(double X, double Y)
        {
            _BasicPoint = (IBasicPoint)new MapWindow.Geometries.Point(X, Y);
            _Position = new PointF(Convert.ToSingle(X), Convert.ToSingle(Y));
            _PointSymbolizer = new PointSymbolizer();
            FeatureSymbolizer = _PointSymbolizer;
            _IsEmpty = true;
        }

        /// <summary>
        /// This creates a new map point from the specified point feature with
        /// null characteristics and the IsEmpty property set to true.
        /// </summary>
        /// <param name="fromFeature">The point feature to create into a MapPoint</param>
        public MapPoint(IFeature fromFeature) : base(fromFeature)
        {
            _Position = new PointF(Convert.ToSingle(fromFeature.BasicGeometry.Coordinates[0].X), Convert.ToSingle(fromFeature.BasicGeometry.Coordinates[0].Y));
            _BasicPoint = (IBasicPoint)fromFeature.BasicGeometry;
            _PointSymbolizer = new PointSymbolizer();
            FeatureSymbolizer = _PointSymbolizer;
            _IsEmpty = true;
        }

        /// <summary>
        /// Rather than having a number of objects created equal to the number of points in the shapefile,
        /// This strategy allows a single symbolizer to work for all the points.  The _IsEmpty field
        /// is true until one of the MapPoint's properties is set directly, at which time, a new
        /// blank symbolizer is created with the exception of the specified field, and it's default
        /// symbolizer is set to the previous one.
        /// </summary>
        /// <param name="fromFeature"></param>
        /// <param name="DefaultSymbolizer"></param>
        public MapPoint(IFeature fromFeature, IPointSymbolizer DefaultSymbolizer)
        {
            _Position = new PointF(Convert.ToSingle(fromFeature.BasicGeometry.Coordinates[0].X), Convert.ToSingle(fromFeature.BasicGeometry.Coordinates[0].Y));
            _BasicPoint = (IBasicPoint)fromFeature.BasicGeometry;
            _PointSymbolizer = DefaultSymbolizer;
            FeatureSymbolizer = _PointSymbolizer;
            _PointSymbolizer.Name = "Go Go Gadget";
            _IsEmpty = true;
        }

        /// <summary>
        /// Constructor for the MapPoint class
        /// </summary>
        /// <param name="fromFeature">The geometry and attribute information for this MapPoint</param>
        /// <param name="DefaultSymbolizer">Default Drawing Characteristics, like from a layer</param>
        public MapPoint(IFeature fromFeature, IFeatureSymbolizer DefaultSymbolizer)
        {
            _Position = new PointF(Convert.ToSingle(fromFeature.BasicGeometry.Coordinates[0].X), Convert.ToSingle(fromFeature.BasicGeometry.Coordinates[0].Y));
            _BasicPoint = (IBasicPoint)fromFeature.BasicGeometry;
            _PointSymbolizer = new PointSymbolizer(DefaultSymbolizer);
            _FeatureSymbolizer = _PointSymbolizer;
            _IsEmpty = true;
        }


        #endregion


        #region Methods

        /// <summary>
        /// This will clear the drawing characteristics of this MapPoint
        /// </summary>
        public new void Clear()
        {
            _PointSymbolizer.Clear();
        }


        

        /// <summary>
        /// Copies all the point symbol properties from any valid implementation of IPointFeatureSymbology
        /// like a MapPoint or a MapPointLayer 
        /// </summary>
        /// <param name="copyFrom"></param>
        public void Copy(IPointSymbolizer copyFrom)
        {
            _PointSymbolizer.Copy(copyFrom);
        }

        /// <summary>
        /// Creates an image using the symbol properties and returns an appropriate texture.
        /// </summary>
        /// <param name="device">The Microsoft.DirectX.Direct3D device for controlling texture creation</param>
        /// <returns>A Texture that should represent the symbol for this icon</returns>
        public Texture CreateTexture(Device device)
        {
            return _PointSymbolizer.CreateTexture(device);
        }
      

        /// <summary>
        /// Returns the 2D Euclidean distance from <c>p</c> coordinate.
        /// </summary>
        /// <param name="p"><c>Coordinate</c> with which to do the distance comparison.</param>
        /// <returns>Double, the 2D Distance to compare with</returns>
        public virtual double Distance(ICoordinate p)
        {
            return _BasicPoint.Distance(p);
        }


        

        /// <summary>
        /// This is a special case where we aren't interested in the normal positioning
        /// of the point, but we just want to draw the symbol for the
        /// point in the specified target location.  No changes will be made to the
        /// graphics object, however, so care should be taken that the transforms
        /// suit your needs before calling this.  Size will be more or less ignored.
        /// </summary>
        /// <param name="g">The System.Drawing.Graphics surface to draw on.</param>
        /// <param name="clipRectangle">The bounds within which to draw</param>
        /// <param name="target">This specifies the pixel "Fit To" rectangle</param>
        /// <exception cref="ArgumentNullException">g cannot be null.</exception>
        /// <exception cref="ArgumentNullException">Target cannot be empty.</exception>
        public override void Draw(System.Drawing.Graphics g, Rectangle clipRectangle, Rectangle target)
        {
            try
            {
                // Immediately before drawing, we update the symbolizer to hold the latest values
                // from the parent point layer if necessary
                _PointSymbolizer.Copy(this);
            }
            catch (Exception ex)
            {
                OnPaintCancelled(ex, g, clipRectangle, target);
            }
            _PointSymbolizer.Draw(g, clipRectangle, target);
        }
        
      
       
      

        /// <summary>
        /// Returns whether the planar projections of the two <c>Coordinate</c>s are equal.
        ///</summary>
        /// <param name="other"><c>Coordinate</c> with which to do the 2D comparison.</param>
        /// <returns>
        /// <c>true</c> if the x- and y-coordinates are equal;
        /// the Z coordinates do not have to be equal.
        /// </returns>
        public virtual bool Equals2D(ICoordinate other)
        {
            return _BasicPoint.Equals2D(other);
        }

        /// <summary>
        /// Returns <c>true</c> if <c>other</c> has the same values for x, y and z.
        /// </summary>
        /// <param name="other"><c>Coordinate</c> with which to do the 3D comparison.</param>
        /// <returns><c>true</c> if <c>other</c> is a <c>Coordinate</c> with the same values for x, y and z.</returns>
        public virtual bool Equals3D(ICoordinate other)
        {
            return _BasicPoint.Equals3D(other);
        }

       
        


        /// <summary>
        /// - Not supported until I have a Symbol object later for symbology groups
        /// </summary>
        /// <returns></returns>
        public virtual IPointSymbol ToSymbol()
        {
            throw new Exception("The method or operation is not implemented.");
        }

        #endregion


        #region Properties

        /// <summary>
        /// Gets or sets the float angle, measured counter-clockwise from horizontal - east
        /// for this point.  Setting this to something outside -360 to 360 will use the layer default.
        /// </summary>
        public float Angle
        {
            get { return _PointSymbolizer.Angle; }
            set 
            {
                // Assume we previously referenced a PointLayer, and set the new
                // symbolizer to use the old one for default properties
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _PointSymbolizer.Angle = value;
            }
           
        }

        /// <summary>
        /// Gets or sets the color of the border, or outline for this point.
        /// If this is empty, the default for the layer will be used.
        /// </summary>
        public override System.Drawing.Color BorderColor
        {
            // This overrides the default handling to ensure the new 
            // default symbolizer is a PointSymbolizer
            set
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _FeatureSymbolizer.BorderColor = value;
            }
        }

        /// <summary>
        /// Gets or sets the float thickness for the border of this point.
        /// Setting this property will also affect the BorderPen property.
        /// If this is negative or 0, the default layer thickness is used.
        /// </summary>
        public override float BorderWidth
        {
            // This overrides the default handling to ensure the new 
            // default symbolizer is a PointSymbolizer
            set
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _FeatureSymbolizer.BorderWidth = value;
            }
        }

        /// <summary>
        /// Gets or sets the pen to use with the this point to draw the border.
        /// Setting this will also reset the BorderColor proprety.
        /// </summary>
        public override System.Drawing.Pen BorderPen
        {
            // This overrides the default handling to ensure the new 
            // default symbolizer is a PointSymbolizer
            set
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _FeatureSymbolizer.BorderPen = value;
            }
        }

        /// <summary>
        /// Gets or sets a boolean value that determines whether or not the default points for this layer
        /// will draw a border.  If this property is set to false, the other border properties will be ignored.
        /// </summary>
        public override bool BorderVisible
        {
            // This overrides the default handling to ensure the new 
            // default symbolizer is a PointSymbolizer
            set
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _FeatureSymbolizer.BorderVisible = value;
            }
        }

        /// <summary>
        /// Gets or sets the color for a dot that will be placed at the center of this point.
        /// </summary>
        public System.Drawing.Color DotColor
        {
            get { return _PointSymbolizer.DotColor; }
            set 
            {
                // Assume we previously referenced a PointLayer, and set the new
                // symbolizer to use the old one for default properties
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    
                    _IsEmpty = false;
                }
                _PointSymbolizer.DotColor = value;
            }
        }

        /// <summary>
        /// Gets or sets the float radius for the dot at the center of this point
        /// </summary>
        public float DotRadius
        {
            get { return _PointSymbolizer.DotRadius; }
            set 
            {
                // Assume we previously referenced a PointLayer, and set the new
                // symbolizer to use the old one for default properties
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    
                    _IsEmpty = false;
                }
                _PointSymbolizer.DotRadius = value;
            }
        }

        /// <summary>
        /// Gets or sets a boolean value indicating whether or not the dot will be visible for this point.
        /// </summary>
        public bool DotVisible
        {
            get { return _PointSymbolizer.DotVisible; }
            set 
            {
                // Assume we previously referenced a PointLayer, and set the new
                // symbolizer to use the old one for default properties
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _PointSymbolizer.DotVisible = value;
            }
        }

        /// <summary>
        /// Gets or sets the solid color to be used for filling this specific point.
        /// This will also change the FillBrush to a solid brush of the specified color.
        /// </summary>
        public override System.Drawing.Color FillColor
        {
            // This overrides the default handling to ensure the new 
            // default symbolizer is a PointSymbolizer
            set
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _FeatureSymbolizer.FillColor = value;
            }
        }

        /// <summary>
        /// Gets or sets the System.Drawing.Brush to be used when filling this point.
        /// Setting this value will also change the FillColor property, but only if
        /// the brush is a System.Drawing.SolidBrush.  Otherwise, the color property
        /// will be ignored.
        /// </summary>
        public override System.Drawing.Brush FillBrush
        {
            // This overrides the default handling to ensure the new 
            // default symbolizer is a PointSymbolizer
            set
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _FeatureSymbolizer.FillBrush = value;
            }
        }
       


        /// <summary>
        /// Gets or sets a true-size icon to be used if the Shape property
        /// is set to Image.  This will draw the icon
        /// without changing it to fit the point size or target.
        /// </summary>
        public System.Drawing.Icon Icon
        {
            get { return _PointSymbolizer.Icon; }
            set 
            {
                // Assume we previously referenced a PointLayer, and set the new
                // symbolizer to use the old one for default properties
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _PointSymbolizer.Icon = value;
            }
            
        }

        /// <summary>
        /// Gets or sets a System.Drawing.Image to draw in the case
        /// where the Shape is set to Image.  This will draw a scaled version 
        /// of the image specified.
        /// </summary>
        public System.Drawing.Image Image
        {
            get { return _PointSymbolizer.Image; }

            set 
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _PointSymbolizer.Image = value;
            }
        }

        /// <summary>
        /// Gets or sets a float value from 0 to 1 where 0 is completely transparent
        /// and 1 is completely solid.  Setting an alpha of a specific feature, like
        /// FillColor, to something less than 255 will control that feature's transparency
        /// without affecting the others.  The final transparency of the feature will
        /// be that alpha multiplied by this value and rounded to the nearest byte.
        /// </summary>
        public override float Opacity
        {
            // Assume we previously referenced a PointLayer, and set the new
            // symbolizer to use the old one for default properties
            set
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _FeatureSymbolizer.Opacity = value;
            }
        }

        /// <summary>
        /// Before drawing to the real world, a sprite is created and used as a texture.
        /// This controls the number of pixels that are on the texture, which will be
        /// stretched in order to fit the real world dimensions.
        /// </summary>
        public System.Drawing.Size PixelSize
        {
            get { return _PointSymbolizer.PixelSize; }
            set { _PointSymbolizer.PixelSize = value; }
        }



        /// <summary>
        /// Returns an IMapPointLayer for this MapPoint's parent layer if one exists.
        /// </summary>
        public IMapPointLayer ParentPointFeatureLayer
        {
            get { return _Parent; }
            set 
            {
                _Parent = value;
            }
        }
        
        /// <summary>
        /// Allows the symbolizer to be set to an existing symbolizer, rather than
        /// the unique symbolizer.
        /// </summary>
        public IPointSymbolizer PointSymbolizer
        {
            get { return _PointSymbolizer; }
            set 
            {
                _PointSymbolizer = value;
                _FeatureSymbolizer = (IFeatureSymbolizer)value;
            }
        }

        /// <summary>
        /// Since most of the drawing for GDI graphics is done in float, this allows
        /// automatic caching of a PointF variable of that format.
        /// </summary>
        public System.Drawing.PointF Position
        {
            get
            {
                if (_Position == System.Drawing.PointF.Empty)
                {
                    _Position = new System.Drawing.PointF(
                        System.Convert.ToSingle(this.BasicGeometry.Coordinates[0].X),
                        System.Convert.ToSingle(this.BasicGeometry.Coordinates[0].Y));
                }
                return _Position;
            }
            set
            {
                BasicGeometry.Coordinates[0].X = System.Convert.ToSingle(value.X);
                BasicGeometry.Coordinates[0].Y = System.Convert.ToSingle(value.Y);
                _Position = value;
            }
        }

        /// <summary>
        /// Gets or Sets a ScaleModes enumeration that determines whether non-coordinate drawing
        /// properties like width or size use pixels or world coordinates.  If pixels are
        /// specified, a back transform is used to approximate pixel sizes. 
        /// </summary>
        public override ScaleModes ScaleMode
        {
            // Assume we previously referenced a PointLayer, and set the new
            // symbolizer to use the old one for default properties
            set
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _FeatureSymbolizer.ScaleMode = value;
            }
        }

        


        /// <summary>
        /// A MapWindow.Drawing.PointShapes enumeration to clarify what kind of points
        /// to draw for the image.
        /// </summary>
        public PointShapes Shape
        {
            get { return _PointSymbolizer.Shape; }
            set
            { 
                _PointSymbolizer.Shape = value;
                // Assume we previously referenced a PointLayer, and set the new
                // symbolizer to use the old one for default properties
                if (_IsEmpty == true)
                {
                    _PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _FeatureSymbolizer = _PointSymbolizer;
                    _IsEmpty = false;
                }
            }
        }
        
        /// <summary>
        /// Gets or sets the System.Drawing.SizeF in pixels to make this MapPoint.
        /// </summary>
        public System.Drawing.SizeF Size
        {
            get { return _PointSymbolizer.Size; }
            set 
            { 
                _PointSymbolizer.Size = value;
                // Assume we previously referenced a PointLayer, and set the new
                // symbolizer to use the old one for default properties
                if (_IsEmpty == true)
                {
                    _PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _FeatureSymbolizer = _PointSymbolizer;
                    _IsEmpty = false;
                }
            }
        }

        /// <summary>
        /// Gets or sets a boolean indicating whether or not this specific MapPoint should be drawn.
        /// </summary>
        public override bool Visible
        {
            // Assume we previously referenced a PointLayer, and set the new
            // symbolizer to use the old one for default properties
            set
            {
                if (_IsEmpty == true)
                {
                    PointSymbolizer = new PointSymbolizer(_PointSymbolizer);
                    _IsEmpty = false;
                }
                _FeatureSymbolizer.Visible = value;
            }
        }

        /// <summary>
        /// Gets or sets the double X or horizontal or East/West coordinate for this map point
        /// </summary>
        public virtual double X
        {
            get { return _BasicPoint.X; }
            set { _BasicPoint.X = value; }
        }

        /// <summary>
        /// Gets or sets the double Y or vertical or North/South coordinage for this map point
        /// </summary>
        public virtual double Y
        {
            get { return _BasicPoint.Y; }
            set { _BasicPoint.Y = value; }
        }

        /// <summary>
        /// Gets or sets the double Z or Up/Down coordinate for this map point
        /// </summary>
        public virtual double Z
        {
            get { return _BasicPoint.X; }
            set { _BasicPoint.X = value; }
        }

        #endregion


        /// <summary>
        /// Orders points by the row major raster scan sequence
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public virtual int CompareTo(object o)
        {
            ICoordinate other = o as ICoordinate;
            if (other == null) return 0;
            if (other.Y > this.Y) return 1;
            if (other.Y < this.Y) return -1;
            if (other.X > this.X) return 1;
            if (other.X < this.X) return -1;
            return 0;
        }
       

    }
}
